package com.anlu.base.async;

import com.alibaba.ttl.TtlCallable;
import com.alibaba.ttl.TtlRunnable;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * <p>
 * Description: 多线程执行的工具类
 * <p>
 * <p>
 *
 * @author anlu
 * @date 2024/9/23 14:31
 */
public class ExecutorUtils {


    public static MyThreadPoolTaskExecutor newSingleThreadPoolTaskExecutor() {
        return newSingleThreadPoolTaskExecutor(System.nanoTime() + "");
    }

    /**
     * 四种拒绝策略都适用于什么场景
     * 因为技术永远没有最好，只有最适合，所以基于上述咱们对拒绝策略的分析，咱们可以大致可以将拒绝策略分别使用到如下场景，
     *
     * CallerRunsPolicy经分析触发时会阻塞主线程，所以像咱们插入一些操作日志对吧，咱们就可以采用该策略，因为插入就算阻塞主线程，对应用也不会有太大影响。
     *
     * DiscardOldestPolicy我们就可以用在报表导出，像咱们导出时不想阻塞线程，避免前端等待过久，咱们都会采用异步的方式（文件导出后写入到表中，业务可以后续在页面上查看）去导出对吧，那么就可以采用该策略，因为就算丢弃了咱们可以重新触发嘛。
     *
     * AbortPolicy（线程池默认的拒绝策略）一触发就报错，一般应用于一些对数据处理非常敏感，例如对一些数据落盘时，数据插入可能有多个逻辑处理，并且还要存储多个地方，例如mysql、redis、es，而且这些如果落盘失败可能会导致业务后续处理失败，那么咱们就可以采用该策略，资源不足时立即报错，阻止一些脏数据落库，避免影响整体业务流程，问题早发现早解决。
     *
     * DiscardPolicy也可以应用于数据导出这种场景，但是尽量不要应用于插入操作日志这种场景，特别是对于一些敏感类的操作日志
     * @param threadNamePrefix
     * @return
     */
    /**
     * 方法描述: 单线程
     * 
     * @param threadNamePrefix .
     * @return com.anlu.base.async.ExecutorUtils.MyThreadPoolTaskExecutor
     * @author 安路路
     * @date 2024/9/23  14:47
     */
    private static MyThreadPoolTaskExecutor newSingleThreadPoolTaskExecutor(String threadNamePrefix) {
        MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(1);
        executor.setQueueCapacity(0);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("single-thread-" + threadNamePrefix);
        /**
         * CallerRunsPolicy:调用主线程来执行:
         *   已提交第一个任务..
         *   已提交第二个任务..
         *   线程池资源即将耗尽..
         *   触发拒绝策略，该方法将由主线程同步执行
         *
         * DiscardOldestPolicy:即将等待最久的任务丢弃，运行效果如下：
         *  已提交第一个任务..
         *  已提交第二个任务..
         *  线程池资源即将耗尽..
         *  线程池已将等待最久一个任务丢弃，我会被立马执行0
         *  触发拒绝策略，等待最久的一个任务将被丢弃,当前任务数量：2
         *
         * AbortPolicy和DiscardPolicy: 直接抛出异常
         *      Exceptioninthread "main" java.util.concurrent.RejectedExecutionException: Taskjava.util.concurrent.FutureTask@7c0e2abd rejected from java.util.concurrent.ThreadPoolExecutor@5ae9a829[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
         *      at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        return executor;
    }


    /**
     * 初始化一个和cpu核数一样的线程池
     * @param threadNamePrefix
     * @return
     */
    public static MyThreadPoolTaskExecutor cpuExecutor(String threadNamePrefix){
        MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor();
        //获取当前服务器的cpu数量
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(corePoolSize);
        executor.setQueueCapacity(0);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("cpu-" + corePoolSize + "-thread-" + threadNamePrefix + "-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;

    }


    public static class MyThreadPoolTaskExecutor extends org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor {

        private int coreSize;

        public MyThreadPoolTaskExecutor(int coreSize) {
            this.coreSize = coreSize;
        }

        public MyThreadPoolTaskExecutor() {
        }



        @Override
        public void execute(Runnable task) {
            Runnable cc = () -> {
                if (null == task) {
                    return;
                }
                task.run();
            };

            TtlRunnable ttlRunnable = TtlRunnable.get(cc);
            super.execute(ttlRunnable);
        }

        @Override
        public Future<?> submit(Runnable task) {
            Runnable cc = () -> {
                if (null == task) {
                    return;
                }
                task.run();
            };
            TtlRunnable ttlRunnable = TtlRunnable.get(cc);
            return super.submit(ttlRunnable);
        }

        @Override
        public <T> Future<T> submit(Callable<T> task) {

            Callable<T> cc = () -> {
                if (null == task) {
                    return null;
                }

                return task.call();
            };

            TtlCallable<T> ttlCallable = TtlCallable.get(cc);
            return super.submit(ttlCallable);
        }
    }
}
